﻿/*
 * LINQ to LDAP
 * http://linqtoldap.codeplex.com/
 * 
 * Copyright Alan Hatter (C) 2010-2012
 * alan.hatter@gmail.com - http://hattercoding.blogspot.com/
 * 
 * This project is subject to licensing restrictions. Visit http://linqtoldap.codeplex.com/license for more information.
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using LinqToLdap.Collections;
using LinqToLdap.Exceptions;

namespace LinqToLdap.Mapping
{
    internal abstract class ObjectMapping : IObjectMapping
    {
        private readonly ReadOnlyDictionary<string, IPropertyMapping> _propertyMappings;
        private readonly ReadOnlyDictionary<string, string> _propertyNames;
        private readonly IPropertyMapping _distinguishedName;
        private readonly ReadOnlyCollection<IPropertyMapping> _updateablePropertyMappings;

        protected ObjectMapping(string namingContext, IEnumerable<IPropertyMapping> propertyMappings,
            string objectCategory = null, IEnumerable<string> objectClass = null)
        {
            NamingContext = namingContext;
            ObjectCategory = objectCategory;
            ObjectClasses = objectClass;
            _propertyNames = new ReadOnlyDictionary<string, string>(
                propertyMappings.Select(p => new { Property = p.PropertyName, Attribute = p.AttributeName })
                                .ToDictionary(p => p.Property, p => p.Attribute));

            _propertyMappings = new ReadOnlyDictionary<string, IPropertyMapping>(
                propertyMappings.ToDictionary(pm => pm.PropertyName));

            _distinguishedName = propertyMappings.FirstOrDefault(p => p.IsDistinguishedName);
            _updateablePropertyMappings =
                new ReadOnlyCollection<IPropertyMapping>(
                    _propertyMappings.Values.Where(p => !p.IsStoreGenerated && !p.IsDistinguishedName && !p.IsReadOnly)
                        .ToList());
        }

        public abstract Type Type { get; }
        public abstract bool IsForAnonymousType { get; }

        public string NamingContext { get; private set; }
        public string ObjectCategory { get; private set; }
        public IEnumerable<string> ObjectClasses { get; private set; }
        public ReadOnlyDictionary<string, string> Properties { get { return _propertyNames; } }

        public IEnumerable<IPropertyMapping> GetPropertyMappings()
        {
            return _propertyMappings.Values;
        }

        public IEnumerable<IPropertyMapping> GetUpdateablePropertyMappings()
        {
            return _updateablePropertyMappings;
        }

        public IPropertyMapping GetPropertyMapping(string name)
        {
            IPropertyMapping mapping;
            if (_propertyMappings.TryGetValue(name, out mapping))
            {
                return mapping;
            }

            throw new MappingException(string.Format("Mapping with name '{0}' was not found for '{1}'", name, Type.FullName));
        }

        public IPropertyMapping GetDistinguishedNameMapping()
        {
            return _distinguishedName;
        }

        public abstract object Create(object[] parameters = null);
    }
}
